/*
 Copyright (c) 2013 MeherTJ G. All rights reserved.
 License: LGPL for personnal study or free software.
 */

#pragma once

#include <cstdint>
#include "zen_exception.h"

namespace Zen {
	template<typename _T, size_t _D>
	struct Point_T {
		static_assert(_D >= 1 && _D < 1024, "invalid dimension for template Point");
		using Type = _T;
		static constexpr size_t cSize = _D;
		Type aValues[cSize] {};

		Type const & XValue() const
		{
			return aValues[0];
		}
		Type const & YValue() const
		{
			static_assert(cSize > 1, "No Y");
			return aValues[1];
		}
		Type const & ZValue() const
		{
			static_assert(cSize > 2, "No Z");
			return aValues[2];
		}
		Type const & At(size_t index) const
		{
			return aValues[index];
		}

		void SetXValue(Type x) 
		{ 
			aValues[0] = x;
		}
		void SetYaValue(Type y) 
		{
			static_assert(cSize > 1, "No Y");
			aValues[1] = y;
		}
		void SetZaValue(Type z) 
		{
			static_assert(cSize > 2, "No Z");
			aValues[2] = z;
		}

		static Point_T Make(Type x, Type y)
		{
			static_assert(cSize == 2, "");
			return Point_T{ x, y };
		}

		static Point_T Make(Type x, Type y, Type z)
		{
			static_assert(cSize == 3, "");
			return Point_T{ x, y, z };
		}

		static Point_T Make(std::initializer_list<Type> const & ini)
		{
			static_assert(cSize == ini.size(), "");
			return Point_T(ini);
		}
		friend int operator < (Point_T const & a, Point_T const & b);
	};

	using Pt2i = Point_T<int32_t, 2>;
	using Pt3i = Point_T<int32_t, 2>;
	using Pt2f = Point_T<float, 2>;
	using Pt3f = Point_T<float, 3>;

	struct Point2i {
		int32_t x;
		int32_t y;
	};
	struct Size2i {
		int32_t w;
		int32_t h;
	};

	struct Point2 {
		float x, y;
	};
	
	struct Size2 {
		float w, h;
	};

	struct Point3 {
		float x, y, z;
	};
	
	struct Rect4f
	{
		float x, y, w, h;
	};

	/// point2
	inline Point2 Point2Make(float x, float y)
	{
		return {x, y};
	}
	inline Point2 Point2Make(Size2 size)
	{
		return { size.w, size.h };
	}
	/// size2
	inline Size2 Size2Make(float w, float h)
	{
		return {w, h};
	}
	inline Size2 Size2Make(Point2 p)
	{
		return { p.x, p.y };
	}
	/// point3
	inline Point3 Point3Make(float x, float y, float z)
	{
		return { x, y, z };
	}

	/// rect
	inline Rect4f RectMake(float x, float y, float w, float h)
	{
		return { x, y, w, h };
	}
	inline Rect4f RectMake(Point2 const & p, Size2 const & sz)
	{
		return { p.x, p.y, sz.w, sz.h };
	}
	inline Rect4f RectMake(Point2 const & p0, Point2 const & p1)
	{
		return { p0.x, p0.y, p1.x-p0.x, p1.y-p0.y };
	}
}

inline bool operator == (Zen::Point2 const & a, Zen::Point2 const & b) {
	return a.x == b.x && a.x == b.y;
}
inline bool operator == (Zen::Size2 const & a, Zen::Size2 const & b) {
	return a.w == b.w && a.h == b.h;
}
inline bool operator == (Zen::Point3 const & a, Zen::Point3 const & b) {
	return a.x == b.x && a.y == b.y && a.z == b.z;
}

inline bool operator == (Zen::Point2i const & a, Zen::Point2i const & b) {
	return a.x == b.x && a.x == b.y;
}
inline bool operator == (Zen::Size2i const & a, Zen::Size2i const & b) {
	return a.w == b.w && a.h == b.h;
}

